package com.loong.backend.gateway.filter;


import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.loong.common.enumerate.ResultCode;
import com.loong.common.rest.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.List;


/**
 * 签名验证过滤器
 *
 * 全局过滤器，默认处理所有请求的验证
 * 若需要排除验证的API请在filter/exclude.yml中指定
 */
@Slf4j
public class SignFilter implements GlobalFilter, Ordered {

    /**
     * 密钥
     */
    private final String secret = "global";

    /**
     * 排除签名验证的API
     */
    private List<String> signExclude;

    /**
     * 路径匹配验证器
     */
    private PathMatcher pathMatcher = new AntPathMatcher();

    public SignFilter(List<String> signExclude) {
        this.signExclude = signExclude;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        if (CollectionUtils.isEmpty(signExclude)) {
            return chain.filter(exchange);
        }
        ServerHttpResponse response = exchange.getResponse();
        ServerHttpRequest request = exchange.getRequest();
        // 获取请求的路径
        String requestPath = request.getURI().getRawPath();
        if (check(requestPath)) {
            return chain.filter(exchange);
        }
        // 获取客户端传递的sign字符串
        String sign = request.getHeaders().getFirst("sign");
        if (StringUtils.isEmpty(sign)) {
            sign = request.getQueryParams().getFirst("sign");
        }
        if (StringUtils.isEmpty(sign)) {
            return validateFail(response);
        }
        // 处理参数
        LinkedHashMap<String, String> params = getValidate(request);
        StringBuffer sb = new StringBuffer();
        for (String key : params.keySet()) {
            sb.append(key).append("=").append(params.get(key)).append("&");
        }
        String linkString = sb.toString();
        linkString = org.apache.commons.lang3.StringUtils.substring(linkString, 0, linkString.length() - 1);
        linkString = linkString + secret;
        String serverSign = DigestUtils.md5Hex(linkString.getBytes());
        if (org.apache.commons.lang3.StringUtils.equals(sign, serverSign)) {
            return chain.filter(exchange);
        } else {
            return validateFail(response);
        }
    }

    @Override
    public int getOrder() {
        return Integer.MIN_VALUE;
    }

    private LinkedHashMap<String, String> getValidate(ServerHttpRequest request) {
        LinkedHashMap<String, String> keys = Maps.newLinkedHashMap();
        // 设备号
        String deviceId = request.getHeaders().getFirst("device_id");
        if (StringUtils.isEmpty(deviceId)) {
            deviceId = request.getQueryParams().getFirst("device_id");
        }
        keys.put("device_id", deviceId);
        // 系统类型
        String osType = request.getHeaders().getFirst("os_type");
        if (StringUtils.isEmpty(osType)) {
            osType = request.getQueryParams().getFirst("os_type");
        }
        keys.put("os_type", osType);
        // 时间戳
        String timestamp = request.getHeaders().getFirst("timestamp");
        if (StringUtils.isEmpty(timestamp)) {
            timestamp = request.getQueryParams().getFirst("timestamp");
        }
        keys.put("timestamp", timestamp);
        // 版本号
        String version = request.getHeaders().getFirst("version");
        if (StringUtils.isEmpty(version)) {
            version = request.getQueryParams().getFirst("version");
        }
        keys.put("version", version);
        return keys;
    }

    private Mono<Void> validateFail(ServerHttpResponse response) {
        Result result = Result.fail(ResultCode.UNAUTHORIZED);
        String resultString = JSONObject.toJSONString(result);
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        DataBuffer buffer = response.bufferFactory().wrap(resultString.getBytes(StandardCharsets.UTF_8));
        return response.writeWith(Mono.just(buffer));
    }

    private boolean check(final String requestPath) {
        if (signExclude.contains(requestPath)) {
            return true;
        }
        for (String excludePath : signExclude) {
            if (pathMatcher.match(excludePath, requestPath)) {
                return true;
            }
        }
        return false;
    }

}
